import json
import logging
import os   
import pdb
from dataclasses import dataclass
from typing import Any, List, Callable, Tuple


from spd_launcher.utils import str_is_empty



class ParserConfigError(Exception):
    
    def __init__(self, *args):
        super().__init__(*args)

class SaveConfigError(Exception):
    
    def __init__(self, *args):
        super().__init__(*args)
        
class AppConfigField:
    
    def __init__(self, keys : Tuple[str], v : Any, value_change_handler : Callable[[List[str], Any, Any], None]):
        self.keys = keys 
        self._value_change_handler = value_change_handler
        self.__v = None
        self.set_value(v, False)
        
        
    def set_value(self, value : Any, check_type : bool = True) -> None:
        if self.__v == value:
            return 
        
        if value is None:
            raise ValueError("设置为空值")
        
        if type(self.__v) is not type(value) and check_type:
            raise ValueError("设置的类型不一致")
        
        old_value = self.__v
        self.__v = value
        
        if self._value_change_handler is None:

            return
        self._value_change_handler(self.keys, old_value, value)

 
    def value(self) -> Any:
        return self.__v
       
# appConfig.value.value()
# appConfig.value.set_value()    

# self.config1 = self.__load_value(keys, configs) -> ConfigField

 
class ConfigLoader:
    
    """_summary_
    """
    def __init__(self, config_path : str):
        if str_is_empty(config_path):
            raise ParserConfigError("配置文件路径为空")
        if not os.path.exists(config_path):
            raise ParserConfigError("配置文件不存在")

       
        self._config_path = config_path 
        self._config_json = {}
        
        self.reload() 
       
    def _load_value(self, *keys : Tuple[str], require : bool = True,except_type : type = None, default : Any = None) -> AppConfigField:
        if keys is None or len(keys) < 1:
            raise ParserConfigError("加载值未设置键") 

        current_obj = self._config_json
        for i in keys:
            
            if i not in current_obj.keys():
                
                if require:
                    raise ParserConfigError(f"配置文件的键{self.format_keys(*keys)}值不存在")
                   
                if not default:
                    logging.warning(f"配置文件中不存在{self.format_keys(*keys)}的键") 
                
                return AppConfigField(keys, default, self.__value_change_handler)
            current_obj = current_obj[i]
        
        
        if except_type and not isinstance(current_obj ,except_type):
            raise ParserConfigError(f"配置文件字段{self.format_keys(*keys)}与期望的类型{except_type}不一致")
        
        return AppConfigField(keys, current_obj, self.__value_change_handler)

    def __value_change_handler(self, keys : Tuple[str], old_value : Any, new_value : Any) -> None:
               
        if old_value == new_value:
            return
        
        current_obj = self._config_json
        
        if len(keys) < 0:
            raise SaveConfigError(f"未设置键")

        if len(keys) == 1:
            return self._config_json[keys[0]]

        last_key = keys[-1]
        
        for i in keys[:-1]:
            if i not in current_obj.keys():
                raise SaveConfigError(f"不存在的键{self.format_keys(keys)}")
            current_obj = current_obj[i]

        current_obj[last_key] = new_value    


    def reload(self):
        with open(self._config_path, "r", encoding="utf-8") as f:
            try:
                self._config_json = json.load(f)
            except json.JSONDecodeError:
                raise ParserConfigError("解析配置文件失败，配置的json格式不合法")
        
        if not self._config_json or self._config_json == {}:
            raise ValueError(f"读取配置文件{self._config_path}失败，内容为空")
 

    @staticmethod  
    def format_keys(*keys) -> str:
        
        if len(keys) <= 0:
             return ""
        
        result = "" 
        for i in keys:
            result = result + i + ":"
        return result[0:-1]
            
    def save_config(self) -> None:
        with open(self._config_path, "w", encoding="utf-8") as f:
            json.dump(self._config_json, f)
            
    
    
if __name__ == "__main__":
    print(ConfigLoader.format_keys())